/*
Copyright (C) 1996-1997 Id Software, Inc.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 3
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/
// gl_vidnt.c -- NT GL vid component

#include "quakedef.h"
#include "menu.shared.h"

extern cvar_t *vid_mode;
extern cvar_t *vid_fullscreen;
extern cvar_t *vid_width;
extern cvar_t *vid_height;
extern cvar_t *vid_vsync;

// for windowed mode resolutions
CMenuValueList vid_WindowedWidth;
CMenuValueList vid_WindowedHeight;

CMenuValueList vid_AnisotropicModes;


// helper functions
char *VID_GetResolution (int mode)
{
	return va ("%ix%i", vid.ModeList[mode].Width, vid.ModeList[mode].Height);
}


char *VID_GetRefreshRate (int mode)
{
	return va ("%fHz", (float) vid.ModeList[mode].RefreshRate.Numerator / (float) vid.ModeList[mode].RefreshRate.Denominator);
}


char *VID_GetFormat (int mode)
{
	switch (vid.ModeList[mode].Format)
	{
	case DXGI_FORMAT_R8G8B8A8_TYPELESS: return "DXGI_FORMAT_R8G8B8A8_TYPELESS";
	/*
	case D3DFMT_X1R5G5B5: return "X1R5G5B5";
	case D3DFMT_R5G6B5: return "R5G6B5";
	case D3DFMT_X8R8G8B8: return "X8R8G8B8";
	case D3DFMT_A2R10G10B10: return "A2R10G10B10";
	*/
	default: return "UNKNOWN";
	}
}


/*
=================
VID_GetModeDescription
=================
*/
char *VID_GetModeDescription (int mode)
{
	// bound
	if (mode < 0) mode = 0;
	if (mode >= vid.NumModes - 1) mode = vid.NumModes - 1;

	return va ("%s (%s/%s)", VID_GetResolution (mode), VID_GetRefreshRate (mode), VID_GetFormat (mode));
}


/*
=================
VID_DescribeCurrentMode_f
=================
*/
void VID_DescribeCurrentMode_f (void)
{
	if (!vid_fullscreen->boolean)
	{
		int width = vid.ModeList[vid_mode->integer].Width;
		int height = vid.ModeList[vid_mode->integer].Height;

		if (vid_width->integer > 0) width = vid_width->integer;
		if (vid_height->integer > 0) height = vid_height->integer;

		Con_Printf (PRINT_DEFAULT, "%ix%i (windowed)\n", width, height);
	}
	else Con_Printf (PRINT_DEFAULT, "%s\n", VID_GetModeDescription (vid_mode->integer));
}


/*
=================
VID_NumModes_f
=================
*/
void VID_NumModes_f (void)
{
	if (vid.NumModes == 1)
		Con_Printf (PRINT_DEFAULT, "%d video mode is available\n", vid.NumModes);
	else Con_Printf (PRINT_DEFAULT, "%d video modes are available\n", vid.NumModes);
}


/*
=================
VID_DescribeMode_f
=================
*/
void VID_DescribeMode_f (void)
{
	Con_Printf (PRINT_DEFAULT, "%s\n", VID_GetModeDescription (Q_atoi (Cmd_Argv (1))));
}


/*
=================
VID_DescribeModes_f
=================
*/
void VID_DescribeModes_f (void)
{
	int	i;

	for (i = 0; i < vid.NumModes; i++)
	{
		Con_Printf (
			PRINT_DEFAULT,
			"%2d: %s\n",
			i,
			VID_GetModeDescription (i)
		);
	}
}


void VID_InitCmds (void)
{
	Cmd_AddCommand ("vid_nummodes", VID_NumModes_f);
	Cmd_AddCommand ("vid_describecurrentmode", VID_DescribeCurrentMode_f);
	Cmd_AddCommand ("vid_describemode", VID_DescribeMode_f);
	Cmd_AddCommand ("vid_describemodes", VID_DescribeModes_f);
}


int vid_mode_option = 0;
BOOL vid_fullscreen_option = FALSE;

char *vid_texfilters[] = {
	"GL_NEAREST",
	"GL_LINEAR",
	"GL_NEAREST_MIPMAP_NEAREST",
	"GL_LINEAR_MIPMAP_NEAREST",
	"GL_NEAREST_MIPMAP_LINEAR",
	"GL_LINEAR_MIPMAP_LINEAR"
};

static const int vid_maxtexfilters = sizeof (vid_texfilters) / sizeof (vid_texfilters[0]);
int vid_texfilter_option = 0;
extern cvar_t *gl_texturemode;

void VID_UpdateVideoMenu (void)
{
	if (key_dest == key_menu && m_state == m_video)
	{
		// update the options in the windowed modes menu
		vid_WindowedWidth.Append (vid_width->integer);
		vid_WindowedHeight.Append (vid_height->integer);
	}
}


void VID_MenuApplySettings (void)
{
	// reset the cvars (the next frame will commit them)
	Cvar_SetValue (vid_mode, vid_mode_option);
	Cvar_SetValue (vid_fullscreen, vid_fullscreen_option ? 1 : 0);
	Cvar_Set (gl_texturemode, vid_texfilters[vid_texfilter_option]);

	// update the windowed mode cvars too
	vid_WindowedWidth.SetCvar (vid_width);
	vid_WindowedHeight.SetCvar (vid_height);
	vid_AnisotropicModes.SetCvar (gl_texture_anisotropy);

	// go back to the options menu after applying changes
	// (will be changed to this->LeaveMenu when this is ported over)
	menus[m_options]->LeaveMenu ();
}


void VID_AdjustFullscreenMode (int dir)
{
	vid_mode_option += dir;

	if (vid_mode_option < 0) vid_mode_option = 0;
	if (vid_mode_option >= vid.NumModes) vid_mode_option = vid.NumModes - 1;
}


#define TAG_FULLSCREEN	1
#define TAG_WINDOWED	2

class qmenu_video_t : public qmenu_t
{
private:
	class qmenu_valuelist_t : public qmenu_option_t
	{
	public:
		qmenu_valuelist_t (char *text, CMenuValueList &mvl)
		{
			Q_strncpy (this->Text, text, sizeof (this->Text) - 1);
			this->ValueList = &mvl;
			this->Active = TRUE;
		}

		int Draw (int y)
		{
			M_DrawStringOption (y, this->Text, va ("%i", this->ValueList->GetCurrent ()));
			return y + 10;
		}

		BOOL Key (int key)
		{
			if (key == K_LEFTARROW)
			{
				this->ValueList->Adjust (-1);
				return TRUE;
			}
			else if (key == K_RIGHTARROW)
			{
				this->ValueList->Adjust (1);
				return TRUE;
			}
			else return FALSE;
		}

		void DrawHighlight (int y)
		{
			M_DrawHighlight (y, 320);

			char *text = va ("%i", this->ValueList->GetCurrent ());
			int x = 164 + ((vid.Width - 320) >> 1);
			int c = 12 + ((int) (realtime * 4) & 1);

			Draw_BackwardsCharacter (x, y + menu_ofsy, c);
			Draw_Character (x + strlen (text) * 8 + 16, y + menu_ofsy, c);
		}

	private:
		CMenuValueList *ValueList;
	};

	class qmenu_fullscreen_watcher_t : public qmenu_checkboxwatcher_t
	{
	public:
		BOOL GetFunc (void) {return vid_fullscreen_option;}
		void SetFunc (void) {vid_fullscreen_option = !vid_fullscreen_option;}
	} fullscreen_watcher;
	
	class qmenu_vsync_watcher_t : public qmenu_checkboxwatcher_t
	{
	public:
		BOOL GetFunc (void) {return vid_vsync->boolean;}
		void SetFunc (void) {Cvar_SetValue (vid_vsync, !vid_vsync->value);}
	} vsync_watcher;
	
public:
	qmenu_video_t (void)
	{
		// add our options
		this->AddOption (new qmenu_banner_t ("gfx/vidmodes.lmp"));
		this->AddOption (new qmenu_divider_t ());

		// these options need explicit apply
		this->AddOption (new qmenu_checkbox_t ("Fullscreen", fullscreen_watcher));
		this->AddOption (new qmenu_valuelist_t ("Video width", vid_WindowedWidth), TAG_WINDOWED);
		this->AddOption (new qmenu_valuelist_t ("Video height", vid_WindowedHeight), TAG_WINDOWED);
		this->AddOption (new qmenu_divider_t ());

		this->AddOption (new qmenu_action_t ("Apply changes", VID_MenuApplySettings));
		this->AddOption (new qmenu_divider_t ());

		// these options apply immediately
		this->AddOption (new qmenu_checkbox_t ("Vertical sync", vsync_watcher));
		this->AddOption (new qmenu_valuelist_t ("Anisotropic filter", vid_AnisotropicModes));

		// and register our menu
		this->Register (m_video);
	}

	void EnterFunc (void)
	{
		vid_fullscreen_option = vid_fullscreen->boolean;

		if (!vid_fullscreen->boolean)
		{
			// find a fullscreen mode that equals or exceeds the current windowed mode
			for (int i = 0; i < vid.NumModes; i++)
			{
				if (vid.ModeList[i].Width < vid_width->integer) continue;
				if (vid.ModeList[i].Height < vid_height->integer) continue;

				vid_mode_option = i;
				break;
			}
		}
		else vid_mode_option = vid_mode->integer;

		// encode filters
		for (int i = 0; i < vid_maxtexfilters; i++)
		{
			if (!Q_strcasecmp (vid_texfilters[i], gl_texturemode->string))
			{
				vid_texfilter_option = i;
				break;
			}
		}

		// set the windowed mode options too - this will add the current modes to the list if not already present,
		// then update the current option item in the list
		vid_WindowedWidth.Append (vid_width->integer);
		vid_WindowedHeight.Append (vid_height->integer);

		// decode anisotropy
		for (int i = 1; ; i <<= 1)
		{
			if (i >= gl_texture_anisotropy->integer)
			{
				if (i < 1)
					vid_AnisotropicModes.Find (1);
				else if (i > 16)
					vid_AnisotropicModes.Find (16);
				else vid_AnisotropicModes.Find (i);

				break;
			}
		}

		this->EnterMenu ();
	}

	BOOL KeyFunc (int key)
	{
		/*
		switch (key)
		{
		case K_ESCAPE:
			this->LeaveMenu ();
			break;

		case K_ENTER:
			switch (this->cursor)
			{
			case 6: VID_MenuApplySettings (); break;
			default: this->AdjustSliders (1); break;
			}

			break;

		case K_UPARROW: this->Navigate (-1); break;
		case K_DOWNARROW: this->Navigate (1); break;
		case K_LEFTARROW: this->AdjustSliders (-1); break;
		case K_RIGHTARROW: this->AdjustSliders (1); break;

		default:
			break;
		}

		return TRUE;
		*/

		return FALSE;
	}

protected:
	void CustomDraw (void)
	{
		if (vid_fullscreen_option)
		{
			this->ShowOptions (TAG_FULLSCREEN);
			this->HideOptions (TAG_WINDOWED);
		}
		else
		{
			this->HideOptions (TAG_FULLSCREEN);
			this->ShowOptions (TAG_WINDOWED);
		}
		/*
		// draw the current option highlight (wider for texture filter modes)
		M_DrawHighlight (this->cursortable[this->cursor], 400);

		// moved this first as it affects the following two
		M_DrawCheckbox (this->cursortable[0], "Fullscreen", vid_fullscreen_option);
		M_DrawDivider (this->cursortable[0] + 12);

		if (vid_fullscreen_option)
		{
			// fullscreen video options
			M_DrawStringOption (this->cursortable[1], "Video mode", VID_GetResolution (vid_mode_option));
			M_DrawStringOption (this->cursortable[2], "Refresh rate", VID_GetRefreshRate (vid_mode_option));
		}
		else
		{
			// windowed video options
			M_DrawValueList (this->cursortable[1], "Video width", vid_WindowedWidth);
			M_DrawValueList (this->cursortable[2], "Video height", vid_WindowedHeight);
		}

		// common options
		M_DrawCheckbox (this->cursortable[3], "Vsync", vid_vsync_option);

		M_DrawDivider (this->cursortable[3] + 12);
		M_DrawStringOption (this->cursortable[4], "Anisotropic Filter", va ("%ix", vid_AnisotropicModes.GetCurrent ()));
		M_DrawStringOption (this->cursortable[5], "Texture Filter", &vid_texfilters[vid_texfilter_option][3]);

		M_DrawDivider (this->cursortable[5] + 12);
		M_DrawCommand (this->cursortable[6], "Accept Changes");
		*/
	}

private:
	void AdjustSliders (int dir)
	{
		/*
		m_soundlevel = m_sound_adjust;

		switch (this->cursor)
		{
		case 0:
			vid_fullscreen_option = !vid_fullscreen_option;
			break;

		case 1:
			if (vid_fullscreen_option)
				VID_AdjustFullscreenMode (dir);
			else vid_WindowedWidth.Adjust (dir);
		
			break;

		case 2:
			if (vid_fullscreen_option)
				VID_AdjustFullscreenMode (dir);
			else vid_WindowedHeight.Adjust (dir);
		
			break;

		case 3:
			vid_vsync_option = !vid_vsync_option;
			break;

		case 4:
			vid_AnisotropicModes.Adjust (dir);
			break;

		case 5:
			vid_texfilter_option += dir;

			if (vid_texfilter_option < 0) vid_texfilter_option = vid_maxtexfilters - 1;
			if (vid_texfilter_option > vid_maxtexfilters - 1) vid_texfilter_option = 0;

			break;

		default:
			break;
		}
		*/
	}
} menu_video;


void VID_InitMenu (void)
{
	// 1x is always available and is equal to no anisotropic filtering
	vid_AnisotropicModes.Append (1);

	// and append the rest of the valid modes
	for (int i = 2; i <= 16; i <<= 1)
		vid_AnisotropicModes.Append (i);

	// append the resolutions
	for (int i = 0; i < vid.NumModes; i++) vid_WindowedWidth.Append (vid.ModeList[i].Width);
	for (int i = 0; i < vid.NumModes; i++) vid_WindowedHeight.Append (vid.ModeList[i].Height);
}


